package com.headwire.kw.helpers;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.NoSuchElementException;

public class XmlWriter
{
	private PrintWriter out;
	private int indent = 0;
	private StackNode stk = null;
	private boolean lastLineEnded = true;
	private boolean tagClosed = true;
	private boolean hasText = false;
	
	public XmlWriter(Writer w) {
		out = new PrintWriter(w);
		out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
	}
	
	public void close() {
		endAllElements();
		out.close();
	}
	
	public void startElement(String element) {
		endl();
		push(element);
		indent();
		out.print("<");
		out.print(element);
		tagClosed = false;
		lastLineEnded = false;
		hasText = false;
		indent++;
		//out.flush();
	}
	
	public void attribute(String name, String value) {
		if(empty()) {
			// no open element!
			throw new NoSuchElementException("No element to add an attribute to!");
		}
		out.print(" "+name+"=");
		out.print("\""+value+"\"");
		//out.flush();
	}
	
	public void text(String text) {
		closeTag();
		out.print(text);
		hasText = true;
		//out.flush();
	}
	
	public void endElement() {
		if(lastLineEnded) {
			// new line, so close top of stack
			indent--;
			indent();
			endTag();
		}
		else {
			indent--;
			// close current tag
			if(hasText) {
				// it has text, full end tag
				endTag();
			}
			else {
				if(!tagClosed) {
					// open tag, no text, do single tag
					out.print(" /");
				}
				endl();
				pop();
			}
		}
	}
	
	public void endAllElements() {
		while(!empty()) {
			endElement();
		}
	}
	
	private void endTag() {
		out.print("</"+pop()+">");
		lastLineEnded = false;
		endl();
	}
	
	private void endl() {
		closeTag();
		if(!lastLineEnded) 
			out.println();
		lastLineEnded = true;
		//out.flush();
	}
	
	private void closeTag() {
		if(!tagClosed)
			out.print(">");
		tagClosed = true;
	}
	
	private void push(String element) {
		stk = new StackNode(element,stk);
	}
	
	private String pop() {
		if(stk == null) {
			throw new NoSuchElementException("Stack is empty.");
		}
		String s = stk.getValue();
		stk = stk.getNext();
		return s;
	}
	
	private boolean empty() {
		return stk == null;
	}
	
	private void indent() {
		for(int i = 0; i < indent; i++) {
			out.print("\t");
		}
	}
	
	class StackNode {
		private String value;
		private StackNode next;
		
		public StackNode(String value) {
			this(value,null);
		}
		
		public StackNode(String value, StackNode next) {
			this.value = value;
			this.next = next;
		}
		
		public String getValue() {
			return value;
		}
		
		public void setNext(StackNode next) {
			this.next = next;
		}
		public StackNode getNext() {
			return next;
		}
		
	}
}